/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "resize_operator.h"

#include <unistd.h>
#include <string>
#include <sys/types.h>
#include <cinttypes>
#include <stdlib.h>
#include "securec.h"
#include "hmfs_common.h"
#include "device_manager.h"
#include "hmfs_io.h"
#include "node.h"
#include "resize_load.h"
#include "resize_defrag.h"

namespace OHOS {
namespace Hmfs {
using namespace std;

ResizeOperator::ResizeOperator(CmdConfig &resizeConfig) : resizeConfig_(resizeConfig)
{
    config_ = std::make_shared<HmfsConfigData>();
    sbInfo_ = std::make_shared<HmfsSbInfo>();
    memset_s(config_.get(), sizeof(HmfsConfigData), 0, sizeof(HmfsConfigData));
    memset_s(sbInfo_.get(), sizeof(HmfsSbInfo), 0, sizeof(HmfsSbInfo));
    HmfsIo::CreateInstance(resizeConfig);
    HmfsCommon::CreateInstance(resizeConfig);
    DeviceManager::CreateInstance(resizeConfig);
    InitConfig();
    reDefrag_ = std::make_unique<ResizeDefrag>(this, sbInfo_, config_);
}

void ResizeOperator::InitConfig()
{
    //init default values
    config_->nDevices = 1;
    config_->sectorsPerBlock = 8;
    config_->blocksPerSegment = 512;
    config_->wantedTotalSectors = -1;
    config_->wantedSectorSize = -1;
    config_->preserveLimits = 1;
    config_->noKernelCheck = 1;
    // struct device_info devices[MAX_DEVICES];
    for (int32_t i = 0; i < MAX_DEVICES; i++) {
        config_->devices[i].fd = -1;
        config_->devices[i].sectorSize = DEFAULT_SECTOR_SIZE;
        config_->devices[i].endBlkId = -1;
        config_->devices[i].zonedModel = HMFS_ZONED_NONE;
    }
    config_->segmentsPerSection = 1;
    config_->sectionsPerZone = 1;
    config_->segmentsPerZone = 1;
    // config_->volumeLabel = "";
    config_->trim = 1;
    config_->kd = 1;
    config_->fixedTime = -1;
    config_->fileEncoding = 0;
    config_->fileEncodingFlags = 0;
    config_->rootUid = getuid();
    config_->rootGid = getgid();
    //command parameter values
    config_->debugLevel = resizeConfig_.debugLevel;
    config_->force = resizeConfig_.force;
    config_->safeResize = resizeConfig_.safeResize;
    config_->targetSectors = resizeConfig_.targetSectors;
    config_->largeNatBitmap = resizeConfig_.largeNatBitmap;
    config_->fileEncoding = resizeConfig_.sEncoding;
    config_->fileEncodingFlags = resizeConfig_.sEncodingFlags;
    config_->feature = resizeConfig_.features;
    config_->newOverprovision = resizeConfig_.newOverprovision;

    config_->sectorSize = 512;
    config_->sectorsPerBlock = HMFS_BLKSIZE / 512;
    /* Get device */
    //if (f2fs_get_device_info() < 0 || f2fs_get_f2fs_info() < 0) { }
}

int32_t ResizeOperator::GetNewSuperBlock(struct SuperBlockData *sbData)
{
    HMFS_DEBUG("Enter GetNewSuperBlock");
    uint32_t zoneSizeBytes;
    uint64_t zoneAlignStartOffset;
    uint32_t blocksForSit, blocksForNat, blocksForSsa;
    uint32_t sitSegments, natSegments, diff, totalMetaSegments;
    uint32_t totalValidBlksAvailable;
    uint32_t sitBitmapSize, maxSitBitmapSize;
    uint32_t maxNatBitmapSize, maxNatSegments;
    uint32_t segmentSizeBytes = 1 << (GetLeValue(sbData->logBlockSize) + GetLeValue(sbData->logBlksPerSeg));
    uint32_t blksPerSeg = 1 << GetLeValue(sbData->logBlksPerSeg);
    uint32_t segsPerZone = GetLeValue(sbData->segsPerSection) * GetLeValue(sbData->sectionsPerZone);

    SetLeValue(sbData->blockCount, config_->targetSectors >> GetLeValue(sbData->logSectorsPerBlk));

    zoneSizeBytes = segmentSizeBytes * segsPerZone;
    zoneAlignStartOffset = ((uint64_t) config_->startSector * DEFAULT_SECTOR_SIZE +
        2 * HMFS_BLKSIZE + zoneSizeBytes - 1) / zoneSizeBytes * zoneSizeBytes -
        (uint64_t) config_->startSector * DEFAULT_SECTOR_SIZE;

    SetLeValue(sbData->segmentCount, (config_->targetSectors * config_->sectorSize - zoneAlignStartOffset) /
        segmentSizeBytes / config_->segmentsPerSection * config_->segmentsPerSection);

    if (!config_->safeResize) {
        blocksForSit = SIZE_ALIGN(GetLeValue(sbData->segmentCount), SIT_ENTRY_PER_BLOCK);
        sitSegments = SIZE_ALIGN(blocksForSit, config_->blocksPerSegment);
        SetLeValue(sbData->segmentCountInSIT, sitSegments * 2);
        SetLeValue(sbData->natBlkId, GetLeValue(sbData->sitBlkId) + GetLeValue(sbData->segmentCountInSIT) * blksPerSeg);

        totalValidBlksAvailable = (GetLeValue(sbData->segmentCount) -(GetLeValue(sbData->segmentCountInCP) +
            GetLeValue(sbData->segmentCountInSIT))) * blksPerSeg;
        blocksForNat = SIZE_ALIGN(totalValidBlksAvailable, NAT_ENTRY_PER_BLOCK);

        if (config_->largeNatBitmap) {
            natSegments = SIZE_ALIGN(blocksForNat, config_->blocksPerSegment) * DEFAULT_NAT_ENTRY_RATIO / 100;
            SetLeValue(sbData->segmentCountInNAT, natSegments ? natSegments : 1);

            maxNatBitmapSize = (GetLeValue(sbData->segmentCountInNAT) << GetLeValue(sbData->logBlksPerSeg)) / 8;
            SetLeValue(sbData->segmentCountInNAT, GetLeValue(sbData->segmentCountInNAT) * 2);
        } else {
            SetLeValue(sbData->segmentCountInNAT, SIZE_ALIGN(blocksForNat, config_->blocksPerSegment));
            maxNatBitmapSize = 0;
        }

        sitBitmapSize = ((GetLeValue(sbData->segmentCountInSIT) / 2) << GetLeValue(sbData->logBlksPerSeg)) / 8;
        if (sitBitmapSize > MAX_SIT_BITMAP_SIZE) {
            maxSitBitmapSize = MAX_SIT_BITMAP_SIZE;
        } else {
            maxSitBitmapSize = sitBitmapSize;
        }
        if (config_->largeNatBitmap) {
            /* use cp_payload if free space of f2fs_checkpoint is not enough */
            if (maxSitBitmapSize + maxNatBitmapSize > MAX_BITMAP_SIZE_IN_CKPT) {
                uint32_t diff =  maxSitBitmapSize + maxNatBitmapSize - MAX_BITMAP_SIZE_IN_CKPT;
                SetLeValue(sbData->cpPayload, HMFS_BLK_ALIGN(diff));
            } else {
                SetLeValue(sbData->cpPayload, 0);
            }
        } else {
            /*
             * It should be reserved minimum 1 segment for nat.
             * When sit is too large, we should expand cp area.
             * It requires more pages for cp.
             */
            if (maxSitBitmapSize > MAX_SIT_BITMAP_SIZE_IN_CKPT) {
                maxNatBitmapSize = MAX_BITMAP_SIZE_IN_CKPT;
                SetLeValue(sbData->cpPayload, HMFS_BLK_ALIGN(maxSitBitmapSize));
            } else {
                maxNatBitmapSize = MAX_BITMAP_SIZE_IN_CKPT - maxSitBitmapSize;
                SetLeValue(sbData->cpPayload, 0);
            }

            maxNatSegments = (maxNatBitmapSize * 8) >> GetLeValue(sbData->logBlksPerSeg);
            if (GetLeValue(sbData->segmentCountInNAT) > maxNatSegments) {
                SetLeValue(sbData->segmentCountInNAT, maxNatSegments);
            }
            SetLeValue(sbData->segmentCountInNAT, GetLeValue(sbData->segmentCountInNAT) * 2);
        }

        SetLeValue(sbData->ssaBlkId, GetLeValue(sbData->natBlkId) + GetLeValue(sbData->segmentCountInNAT) * blksPerSeg);

        totalValidBlksAvailable = (GetLeValue(sbData->segmentCount) - (GetLeValue(sbData->segmentCountInCP) +
            GetLeValue(sbData->segmentCountInSIT) + GetLeValue(sbData->segmentCountInNAT))) * blksPerSeg;

        blocksForSsa = totalValidBlksAvailable / blksPerSeg + 1;

        SetLeValue(sbData->segmentCountInSSA, SIZE_ALIGN(blocksForSsa, config_->blocksPerSegment));

        totalMetaSegments = GetLeValue(sbData->segmentCountInCP) + GetLeValue(sbData->segmentCountInSIT) +
            GetLeValue(sbData->segmentCountInNAT) + GetLeValue(sbData->segmentCountInSSA);

        diff = totalMetaSegments % segsPerZone;
        if (diff) {
            SetLeValue(sbData->segmentCountInSSA, GetLeValue(sbData->segmentCountInSSA) + (segsPerZone - diff));
        }

        SetLeValue(sbData->mainBlkId, GetLeValue(sbData->ssaBlkId) + GetLeValue(sbData->segmentCountInSSA) * blksPerSeg);
    }

    SetLeValue(sbData->segmentCountInMain, GetLeValue(sbData->segmentCount) - (GetLeValue(sbData->segmentCountInCP) +
        GetLeValue(sbData->segmentCountInSIT) + GetLeValue(sbData->segmentCountInNAT) +
        GetLeValue(sbData->segmentCountInSSA)));

    SetLeValue(sbData->sectionCount, GetLeValue(sbData->segmentCountInMain) / GetLeValue(sbData->segsPerSection));

    SetLeValue(sbData->segmentCountInMain, GetLeValue(sbData->sectionCount) * GetLeValue(sbData->segsPerSection));

    /* Let's determine the best reserved and overprovisioned space ifdef HAVE_BLK_ZONE_REP_V2*/ 
    if (config_->newOverprovision == 0) {
        config_->newOverprovision = HmfsCommon::GetInstance().GetBestOverProvision(sbData);
    }
    config_->newReservedSegments = (2 * (100 / config_->newOverprovision + 1) + 6) *
        GetLeValue(sbData->segsPerSection);

    if ((GetLeValue(sbData->segmentCountInMain) - 2) < config_->newReservedSegments ||
        GetLeValue(sbData->segmentCountInMain) * blksPerSeg > GetLeValue(sbData->blockCount)) {
        HMFS_ERROR("\tError: Device size is not sufficient for F2FS volume, more segment needed =%u",
            config_->newReservedSegments - (GetLeValue(sbData->segmentCountInMain) - 2));
        return -1;
    }
    return 0;
}

int32_t ResizeOperator::ShrinkNats(struct SuperBlockData *newSbData)
{
    HMFS_DEBUG("Enter ShrinkNats");
    if (newSbData == nullptr) {
        return -1;
    }
    struct SuperBlockData *sbData = sbInfo_->rawSuper;
    struct NodeAddressTable *nat = sbInfo_->naTable;
    uint32_t oldNatBlkaddr = GetLeValue(sbData->natBlkId);
    int32_t ret = 0;
    void *natBlock = malloc(BLOCK_SZ);
    ASSERT(natBlock);
    void *zeroBlock = calloc(BLOCK_SZ, 1);
    ASSERT(zeroBlock);

    unsigned int natBlocks = GetLeValue(newSbData->segmentCountInNAT) >> 1;
    natBlocks = natBlocks << GetLeValue(sbData->logBlksPerSeg);
    int32_t newMaxNid = NAT_ENTRY_PER_BLOCK * natBlocks;

    for (int32_t nid = nat->maxNid - 1; nid > newMaxNid; nid -= NAT_ENTRY_PER_BLOCK) {
        unsigned long blockOff = nid / NAT_ENTRY_PER_BLOCK;
        int32_t segOff = blockOff >> sbInfo_->logBlksPerSeg;
        unsigned long blockAddr = (unsigned long)(oldNatBlkaddr +
            (segOff << sbInfo_->logBlksPerSeg << 1) +
            (blockOff & ((1 << sbInfo_->logBlksPerSeg) - 1)));

        if (HmfsCommon::GetInstance().HmfsTestBit(blockOff, nat->natBitmap))
            blockAddr += sbInfo_->blocksPerSeg;

        ret = HmfsIo::GetInstance().DevReadBlock(natBlock, blockAddr);
        ASSERT(ret >= 0);

        if (memcmp(zeroBlock, natBlock, BLOCK_SZ)) {
            free(natBlock);
            free(zeroBlock);
            return -1;
        }
    }
    ret = 0;
    nat->maxNid = newMaxNid;
    free(natBlock);
    free(zeroBlock);
    return ret;
}

int32_t ResizeOperator::GetSumEntry(uint32_t blkAddr, struct SummaryEntry *sumEntry)
{
    if (sumEntry == nullptr) {
        return -1;
    }
    int32_t type = 0;
    uint32_t segno = GET_SEGNO(sbInfo_, blkAddr);
    uint32_t blkOffFromMain = blkAddr - SM_I(sbInfo_)->mainBlkaddr;
    uint32_t offset = blkOffFromMain % (1 << sbInfo_->logBlksPerSeg);

    struct SummaryBlockData *sumBlk = GetSumBlock(segno, &type);
    memcpy(sumEntry, &(sumBlk->entries[offset]), sizeof(struct SummaryEntry));
    if (type == SEG_TYPE_NODE || type == SEG_TYPE_DATA || type == SEG_TYPE_MAX) {
        free(sumBlk);
    }
    return type;
}

struct SummaryBlockData* ResizeOperator::GetSumBlock(unsigned int segno, int *ret_type)
{
    if (ret_type == nullptr) {
        return nullptr;
    }
    struct CheckPointData *cpData = sbInfo_->ckptData;
    struct CurSegmentInfo *curseg;
    *ret_type= SEG_TYPE_MAX;

    uint64_t ssaBlk = sbInfo_->smInfoTable->ssaBlkaddr + segno;
    for (int32_t type = 0; type < NR_CURSEG_NODE_TYPE; type++) {
        if (segno == GetLeValue(cpData->curNodeSegNo[type])) {
            curseg = CURSEG_I(sbInfo_, CURSEG_HOT_NODE + type);
            if (!IS_SUM_NODE_SEG(curseg->segSumBlk->footer)) {
                HMFS_ERROR("segno [0x%x] indicates a data "
                        "segment, but should be node",
                        segno);
                *ret_type = -SEG_TYPE_CUR_NODE;
            } else {
                *ret_type = SEG_TYPE_CUR_NODE;
            }
            return curseg->segSumBlk;
        }
    }

    for (int32_t type = 0; type < NR_CURSEG_DATA_TYPE; type++) {
        if (segno == GetLeValue(cpData->curDataSegNo[type])) {
            curseg = CURSEG_I(sbInfo_, type);
            if (IS_SUM_NODE_SEG(curseg->segSumBlk->footer)) {
                HMFS_ERROR("segno [0x%x] indicates a node segment, but should be data", segno);
                *ret_type = -SEG_TYPE_CUR_DATA;
            } else {
                *ret_type = SEG_TYPE_CUR_DATA;
            }
            return curseg->segSumBlk;
        }
    }
    struct SummaryBlockData *sumBlk = static_cast<struct SummaryBlockData *>(calloc(BLOCK_SZ, 1));
    ASSERT(sumBlk);

    int32_t ret = HmfsIo::GetInstance().DevReadBlock(sumBlk, ssaBlk);
    ASSERT(ret >= 0);

    if (IS_SUM_NODE_SEG(sumBlk->footer)) {
        *ret_type = SEG_TYPE_NODE;
    } else if (IS_SUM_DATA_SEG(sumBlk->footer)) {
        *ret_type = SEG_TYPE_DATA;
    }
    return sumBlk;
}

void ResizeOperator::UpdateDataBlkaddr(uint32_t nid, uint16_t ofsInNode, uint32_t newAddr)
{
    struct NodeInfo ni;
    uint32_t oldaddr;

    struct NodeData *nodeBlk = static_cast<struct NodeData *>(calloc(BLOCK_SZ, 1));
    ASSERT(nodeBlk);

    GetNodeInfo(sbInfo_, nid, &ni);

    /* read node_block */
    int ret = HmfsIo::GetInstance().DevReadBlock(nodeBlk, ni.blockAddr);
    ASSERT(ret >= 0);

    /* check its block address */
    if (nodeBlk->footer.nid == nodeBlk->footer.ino) {
        int ofs = HmfsCommon::GetInstance().GetExtraIsize(&nodeBlk->i);

        oldaddr = LE32_TO_NATIVE(nodeBlk->i.i_addr[ofs + ofsInNode]);
        nodeBlk->i.i_addr[ofs + ofsInNode] = NATIVE_TO_LE32(newAddr);
        ret = HmfsCommon::GetInstance().WriteInode(nodeBlk, ni.blockAddr, config_->chksumSeed);
        ASSERT(ret >= 0);
    } else {
        oldaddr = LE32_TO_NATIVE(nodeBlk->dn.addr[ofsInNode]);
        nodeBlk->dn.addr[ofsInNode] = NATIVE_TO_LE32(newAddr);
        ret = HmfsIo::GetInstance().DevWriteBlock(nodeBlk, ni.blockAddr);
        ASSERT(ret >= 0);
    }

    /* check extent cache entry */
    if (nodeBlk->footer.nid != nodeBlk->footer.ino) {
        GetNodeInfo(sbInfo_, LE32_TO_NATIVE(nodeBlk->footer.ino), &ni);

        /* read inode block */
        ret = HmfsIo::GetInstance().DevReadBlock(nodeBlk, ni.blockAddr);
        ASSERT(ret >= 0);
    }

    uint32_t startaddr = LE32_TO_NATIVE(nodeBlk->i.iExt.blkAddr);
    uint32_t endaddr = startaddr + LE32_TO_NATIVE(nodeBlk->i.iExt.len);
    if (oldaddr >= startaddr && oldaddr < endaddr) {
        nodeBlk->i.iExt.len = 0;

        /* update inode block */
        ASSERT(HmfsCommon::GetInstance().WriteInode(nodeBlk, ni.blockAddr, config_->chksumSeed) >= 0);
    }
    free(nodeBlk);
}

void ResizeOperator::UpdateNatBlkaddr(uint32_t ino, uint32_t nid, uint32_t newaddr)
{
    struct natBlockData *natBlock = (struct natBlockData *)calloc(BLOCK_SZ, 1);
    ASSERT(natBlock);

    int entryOff = nid % NAT_ENTRY_PER_BLOCK;
    unsigned long blockAddr = CurrentNatAddr(sbInfo_, nid, NULL);
    int ret = HmfsIo::GetInstance().DevReadBlock(natBlock, blockAddr);
    ASSERT(ret >= 0);

    if (ino)
        natBlock->entries[entryOff].inodeNo = NATIVE_TO_LE32(ino);
    natBlock->entries[entryOff].blockId = NATIVE_TO_LE32(newaddr);
    // if (c.func == FSCK)
    //     F2FS_FSCK(sbi)->entries[nid] = natBlock->entries[entryOff];

    ret = HmfsIo::GetInstance().DevWriteBlock(natBlock, blockAddr);
    ASSERT(ret >= 0);
    free(natBlock);
}

int32_t ResizeOperator::MigrateMainArea(unsigned int offset)
{
    HMFS_DEBUG("Enter MigrateMainArea");
    void *raw = calloc(BLOCK_SZ, 1);
    if (raw == nullptr) {
        return -1;
    }
    SegmentInfoTable *smInfo = sbInfo_->smInfoTable;
    uint32_t smMainSeg = smInfo->mainSegments;
    ASSERT(raw != nullptr);

    for (int32_t i = smMainSeg - 1; i >= 0; i--) {
        struct SegEntry *se = GetSegEntry(sbInfo_, i);
        if (!se->validBlocks) {
            continue;
        }
        for (int32_t j = sbInfo_->blocksPerSeg - 1; j >= 0; j--) {
            if (!HmfsCommon::GetInstance().HmfsTestBit(j, (const char *)se->curValidBitmap)) {
                continue;
            }
            uint32_t from = START_BLOCK(sbInfo_, i) + j;
            int32_t ret = HmfsIo::GetInstance().DevReadBlock(raw, from);
            ASSERT(ret >= 0);

            uint32_t to = from + offset;
            ret = HmfsIo::GetInstance().DevWriteBlock(raw, to);
            ASSERT(ret >= 0);
            struct SummaryEntry sum = {};
            GetSumEntry(from, &sum);

            if (IS_DATASEG(se->type)) {
                UpdateDataBlkaddr(LE32_TO_NATIVE(sum.nid), LE16_TO_NATIVE(sum.ofsInNode), to);
            } else {
                UpdateNatBlkaddr(0, LE32_TO_NATIVE(sum.nid), to);
            }
        }
    }
    free(raw);
    HMFS_INFO("Done to migrate Main area: main_blkaddr = 0x%x -> 0x%x\n",
        START_BLOCK(sbInfo_, 0), START_BLOCK(sbInfo_, 0) + offset);
    return SUCCESS_CODE;
}

void ResizeOperator::MoveSsa(unsigned int segno, uint32_t newSumBlkAddr)
{
    int32_t type = 0;
    struct SummaryBlockData *sumBlk = GetSumBlock(segno, &type);
    if (type < SEG_TYPE_MAX) {
        int32_t ret = HmfsIo::GetInstance().DevWriteBlock(sumBlk, newSumBlkAddr);
        ASSERT(ret >= 0);
        // HMFS_INFO("Write summary block: (%d) segno=%x/%x --> (%d) %x\n",
        //     type, segno, GET_SUM_BLKADDR(sbInfo_, segno),
        //     IS_SUM_NODE_SEG(sumBlk->footer), newSumBlkAddr);
    }
    if (type == SEG_TYPE_NODE || type == SEG_TYPE_DATA || type == SEG_TYPE_MAX) {
        free(sumBlk);
    }
    HMFS_INFO("Done to migrate SSA blocks\n");
}

int32_t ResizeOperator::MigrateSsa(struct SuperBlockData *newSbData, unsigned int offset)
{
    HMFS_DEBUG("Enter MigrateSsa");
    if (newSbData == nullptr) {
        return EXIT_ERR_CODE;
    }
    struct SuperBlockData *sbData = sbInfo_->rawSuper;
    uint32_t oldSumBlkaddr = GetLeValue(sbData->ssaBlkId);
    uint32_t newSumBlkaddr = GetLeValue(newSbData->ssaBlkId);
    uint32_t endSumBlkaddr = GetLeValue(newSbData->mainBlkId);
    uint32_t expandSumBlkaddr = newSumBlkaddr + MAIN_SEGS(sbInfo_) - offset;
    uint32_t blkaddr = 0;
    int32_t ret = -1;
    void *zero_block = calloc(BLOCK_SZ, 1);
    ASSERT(zero_block);

    if (offset && newSumBlkaddr < oldSumBlkaddr + offset) {
        blkaddr = newSumBlkaddr;
        while (blkaddr < endSumBlkaddr) {
            if (blkaddr < expandSumBlkaddr) {
                MoveSsa(offset++, blkaddr++);
            } else {
                ret = HmfsIo::GetInstance().DevWriteBlock(zero_block, blkaddr++);
                ASSERT(ret >=0);
            }
        }
    } else {
        blkaddr = endSumBlkaddr - 1;
        offset = MAIN_SEGS(sbInfo_) - 1;
        while (blkaddr >= newSumBlkaddr) {
            if (blkaddr >= expandSumBlkaddr) {
                ret = HmfsIo::GetInstance().DevWriteBlock(zero_block, blkaddr--);
                ASSERT(ret >=0);
            } else {
                MoveSsa(offset--, blkaddr--);
            }
        }
    }

    HMFS_INFO("Done to migrate SSA blocks: sum_blkaddr = 0x%x -> 0x%x\n",
        oldSumBlkaddr, newSumBlkaddr);
    free(zero_block);
    return SUCCESS_CODE;
}

int32_t ResizeOperator::MigrateNat(struct SuperBlockData *newSbData)
{
    HMFS_DEBUG("Enter MigrateNat");
    if (newSbData == nullptr) {
        return EXIT_ERR_CODE;
    }
    struct SuperBlockData *sbData = sbInfo_->rawSuper;
    struct NodeAddressTable *nat = sbInfo_->naTable;
    uint32_t oldNatBlkaddr = GetLeValue(sbData->natBlkId);
    uint32_t newNatBlkaddr = GetLeValue(newSbData->natBlkId);
    int32_t ret = -1;
    unsigned long blockOff = 0;
    unsigned long blockAddr = 0;
    int32_t segOff = -1;

    void *natBlock = malloc(BLOCK_SZ);
    ASSERT(natBlock);
    for (int32_t nid = nat->maxNid - 1; nid >= 0; nid -= NAT_ENTRY_PER_BLOCK) {
        blockOff = nid / NAT_ENTRY_PER_BLOCK;
        segOff = blockOff >> sbInfo_->logBlksPerSeg;
        blockAddr = (unsigned long)(oldNatBlkaddr + (segOff << sbInfo_->logBlksPerSeg << 1) +
            (blockOff & ((1 << sbInfo_->logBlksPerSeg) - 1)));

        /* move to set #0 */
        if (HmfsCommon::GetInstance().HmfsTestBit(blockOff, nat->natBitmap)) {
            blockAddr += sbInfo_->blocksPerSeg;
            HmfsCommon::GetInstance().HmfsClearBit(blockOff, nat->natBitmap);
        }

        ret = HmfsIo::GetInstance().DevReadBlock(natBlock, blockAddr);
        ASSERT(ret >= 0);

        blockAddr = (unsigned long)(newNatBlkaddr + (segOff << sbInfo_->logBlksPerSeg << 1) +
            (blockOff & ((1 << sbInfo_->logBlksPerSeg) - 1)));

        /* new bitmap should be zeros */
        ret = HmfsIo::GetInstance().DevWriteBlock(natBlock, blockAddr);
        ASSERT(ret >= 0);
    }
    /* zero out newly assigned nids */
    memset(natBlock, 0, BLOCK_SZ);
    unsigned int natBlocks = GetLeValue(newSbData->segmentCountInNAT) >> 1;
    natBlocks = natBlocks << GetLeValue(sbData->logBlksPerSeg);
    int32_t newMaxNid = NAT_ENTRY_PER_BLOCK * natBlocks;

    HMFS_INFO("Write NAT block: %x->%x, max_nid=%x->%x\n",
        oldNatBlkaddr, newNatBlkaddr,
        GetLeValue(sbData->segmentCountInNAT),
        GetLeValue(newSbData->segmentCountInNAT));

    for (int32_t nid = nat->maxNid; nid < newMaxNid; nid += NAT_ENTRY_PER_BLOCK) {
        blockOff = nid / NAT_ENTRY_PER_BLOCK;
        segOff = blockOff >> sbInfo_->logBlksPerSeg;
        blockAddr = (unsigned long)(newNatBlkaddr + (segOff << sbInfo_->logBlksPerSeg << 1) +
            (blockOff & ((1 << sbInfo_->logBlksPerSeg) - 1)));
        ret = HmfsIo::GetInstance().DevWriteBlock(natBlock, blockAddr);
        ASSERT(ret >= 0);
        HMFS_INFO("Write NAT: %lx\n", blockAddr);
    }
    free(natBlock);
    HMFS_INFO("Done to migrate NAT blocks: nat_blkaddr = 0x%x -> 0x%x\n",
        oldNatBlkaddr, newNatBlkaddr);
    return SUCCESS_CODE;
}

int32_t ResizeOperator::MigrateSit(struct SuperBlockData *newSbData, unsigned int offset)
{
    HMFS_DEBUG("Enter MigrateSit");
    if (newSbData == nullptr) {
        return EXIT_ERR_CODE;
    }
    struct SitInfo *sitInfo = SIT_I(sbInfo_);
    unsigned int ofs = 0;
    unsigned int preOfs = 0;
    struct sitBlockData *sitBlk = static_cast<struct sitBlockData *>(calloc(BLOCK_SZ, 1));
    uint32_t sitBlks = GetLeValue(newSbData->segmentCountInSIT) << (sbInfo_->logBlksPerSeg - 1);
    uint32_t blkAddr = 0;
    int32_t ret = 0;
    ASSERT(sitBlk);

    /* initialize with zeros */
    for (unsigned int index = 0; index < sitBlks; index++) {
        ret = HmfsIo::GetInstance().DevWriteBlock(sitBlk, GetLeValue(newSbData->sitBlkId) + index);
        ASSERT(ret >= 0);
        HMFS_INFO("Write zero sit: %x\n", GetLeValue(newSbData->sitBlkId) + index);
    }

    for (unsigned int segno = 0; segno < MAIN_SEGS(sbInfo_); segno++) {
        struct SegEntry *se = GetSegEntry(sbInfo_, segno);
        if (segno < offset) {
            ASSERT(se->validBlocks == 0);
            continue;
        }

        ofs = SIT_BLOCK_OFFSET(sitInfo, segno - offset);

        if (ofs != preOfs) {
            blkAddr = GetLeValue(newSbData->sitBlkId) + preOfs;
            ret = HmfsIo::GetInstance().DevWriteBlock(sitBlk, blkAddr);
            ASSERT(ret >= 0);
            HMFS_INFO("Write valid sit: %x\n", blkAddr);

            preOfs = ofs;
            memset(sitBlk, 0, BLOCK_SZ);
        }
        unsigned int sitEntryOffSet = (segno - offset) % sitInfo->sentsPerBlock;
        struct sitEntry *sit = &sitBlk->entries[sitEntryOffSet];
        memcpy(sit->blockBitmap, se->curValidBitmap, SIT_VBLOCK_MAP_SIZE);
        sit->usedBlockCount = NATIVE_TO_LE16((se->type << SIT_VBLOCKS_SHIFT) | se->validBlocks);
    }
    blkAddr = GetLeValue(newSbData->sitBlkId) + ofs;
    ret = HmfsIo::GetInstance().DevWriteBlock(sitBlk, blkAddr);
    HMFS_INFO("Write valid sit: %x\n", blkAddr);
    ASSERT(ret >= 0);

    free(sitBlk);
    HMFS_INFO("Done to restore new SIT blocks: 0x%x\n", GetLeValue(newSbData->sitBlkId));
    return SUCCESS_CODE;
}

int32_t ResizeOperator::RebuildCheckpoint(struct SuperBlockData *newSbData, unsigned int offset)
{
    HMFS_DEBUG("Enter RebuildCheckpoint");
    if (newSbData == nullptr) {
        return EXIT_ERR_CODE;
    }
    struct CheckPointData *cpData = sbInfo_->ckptData;
    unsigned long long cpVer = GetLeValue(cpData->cpVersion);
    struct SuperBlockData *sbData = sbInfo_->rawSuper;
    unsigned int freeSegmentCount, newSegmentCount;
    uint32_t newCpBlks = 1 + GetLeValue(newSbData->cpPayload);
    uint32_t orphanBlks = 0;
    uint32_t newCpBlkId, oldCpBlkId;
    uint32_t crc = 0;
    int32_t ret = 0;
    struct CheckPointData *newCp = static_cast<struct CheckPointData *>(calloc(newCpBlks * BLOCK_SZ, 1));
    ASSERT(newCp);

    void *buf = malloc(BLOCK_SZ);
    ASSERT(buf);

    /* ovp / free segments */
    SetLeValue(cpData->rsvdSegmentCount, config_->newReservedSegments);
    SetLeValue(cpData->overprovSegmentCount, (GetLeValue(newSbData->segmentCountInMain) -
        GetLeValue(cpData->rsvdSegmentCount)) * config_->newOverprovision / 100);
    SetLeValue(cpData->overprovSegmentCount, GetLeValue(cpData->overprovSegmentCount) +
        GetLeValue(cpData->rsvdSegmentCount));

    HMFS_INFO("Overprovision ratio = %.3lf%%\n", config_->newOverprovision);
    HMFS_INFO("Overprovision segments = %u (GC reserved = %u)\n",
        GetLeValue(cpData->overprovSegmentCount), config_->newReservedSegments);

    freeSegmentCount = GetFreeSegments();
    newSegmentCount = GetLeValue(newSbData->segmentCountInMain) -
        GetLeValue(sbData->segmentCountInMain);

    SetLeValue(cpData->freeSegmentCount, freeSegmentCount + newSegmentCount);
    SetLeValue(cpData->userBlockCount, ((GetLeValue(newSbData->segmentCountInMain) -
        GetLeValue(cpData->overprovSegmentCount)) * config_->blocksPerSegment));

    if (IsCheckpointFlags(cpData, CP_ORPHAN_PRESENT_FLAG)){
        orphanBlks = LE32_TO_NATIVE(cpData->cpPackStartSum) - 1;
    }
    SetLeValue(cpData->cpPackStartSum, 1 + GetLeValue(newSbData->cpPayload));
    SetLeValue(cpData->cpPackBlockCount, 8 + orphanBlks + GetLeValue(newSbData->cpPayload));

    /* cur->segno - offset */
    for (int32_t i = 0; i < NO_CHECK_TYPE; i++) {
        if (i < CURSEG_HOT_NODE) {
            SetLeValue(cpData->curDataSegNo[i], CURSEG_I(sbInfo_, i)->segNum - offset);
        } else {
            int32_t n = i - CURSEG_HOT_NODE;
            SetLeValue(cpData->curNodeSegNo[n], CURSEG_I(sbInfo_, i)->segNum - offset);
        }
    }

    /* sit / nat ver bitmap bytesize */
    SetLeValue(cpData->sitVersionBitmapSize, ((GetLeValue(newSbData->segmentCountInSIT) / 2) <<
        GetLeValue(newSbData->logBlksPerSeg)) / 8);
    SetLeValue(cpData->natVersionBitmapSize, ((GetLeValue(newSbData->segmentCountInNAT) / 2) <<
        GetLeValue(newSbData->logBlksPerSeg)) / 8);

    /* update nat_bits flag */
    uint32_t flags = UpdateNatBitsFlags(newSbData, cpData, GetLeValue(cpData->cpFlags));
    if (config_->largeNatBitmap) {
        flags |= CP_LARGE_NAT_BITMAP_FLAG;
    }
    if (flags & CP_COMPACT_SUM_FLAG) {
        flags &= ~CP_COMPACT_SUM_FLAG;
    }
    if (flags & CP_LARGE_NAT_BITMAP_FLAG) {
        SetLeValue(cpData->checksumOffset, CP_MIN_CHKSUM_OFFSET);
    } else {
        SetLeValue(cpData->checksumOffset, CP_CHKSUM_OFFSET);
    }
    SetLeValue(cpData->cpFlags, flags);
    HMFS_DEBUG("memcpy new checkpoint begin");
    memcpy(newCp, cpData, (unsigned char *)cpData->sitNatVersionBitmap - (unsigned char *)cpData);
    if (config_->safeResize) {
		HMFS_DEBUG("memcpy new checkpoint in safe");
        memcpy(reinterpret_cast<unsigned char *>(newCp) + CP_BITMAP_OFFSET, reinterpret_cast<unsigned char *>(cpData) + CP_BITMAP_OFFSET, HMFS_BLKSIZE - CP_BITMAP_OFFSET);
    }
    newCp->cpVersion = NATIVE_TO_LE64(cpVer + 1);
    crc = HmfsCheckpointChksum(newCp);
    *(reinterpret_cast<uint32_t*>((reinterpret_cast<unsigned char *>(newCp) + GetLeValue(cpData->checksumOffset)))) = NATIVE_TO_LE32(crc);

    /* Write a new checkpoint in the other set */
    newCpBlkId = oldCpBlkId = GetLeValue(sbData->cpBlkId);
    if (sbInfo_->curCpId == 2) {
        oldCpBlkId += 1 << GetLeValue(sbData->logBlksPerSeg);
    } else {
        newCpBlkId += 1 << GetLeValue(sbData->logBlksPerSeg);
    }
    /* write first cp */
    ret = HmfsIo::GetInstance().DevWriteBlock(newCp, newCpBlkId++);
    ASSERT(ret >= 0);

    memset(buf, 0, BLOCK_SZ);
    for (int32_t i = 0; i < GetLeValue(newSbData->cpPayload); i++) {
        ret = HmfsIo::GetInstance().DevWriteBlock(buf, newCpBlkId++);
        ASSERT(ret >= 0);
    }

    for (int32_t i = 0; i < orphanBlks; i++) {
        uint32_t orphan_blk_no = oldCpBlkId + 1 + GetLeValue(sbData->cpPayload);
        ret = HmfsIo::GetInstance().DevReadBlock(buf, orphan_blk_no++);
        ASSERT(ret >= 0);
        ret = HmfsIo::GetInstance().DevWriteBlock(buf, newCpBlkId++);
        ASSERT(ret >= 0);
    }

    /* update summary blocks having nullified journal entries */
    for (int32_t i = 0; i < NO_CHECK_TYPE; i++) {
        struct CurSegmentInfo *curseg = CURSEG_I(sbInfo_, i);
        ret = HmfsIo::GetInstance().DevWriteBlock(curseg->segSumBlk, newCpBlkId++);
        ASSERT(ret >= 0);
    }

    /* write the last cp */
    ret = HmfsIo::GetInstance().DevWriteBlock(newCp, newCpBlkId++);
    ASSERT(ret >= 0);

    /* Write nat bits */
    if (flags & CP_NAT_BITS_FLAG) {
        WriteNatBits(newSbData, newCp, sbInfo_->curCpId == 1 ? 2 : 1);
    }
    /* disable old checkpoint */
    memset(buf, 0, BLOCK_SZ);
    ret = HmfsIo::GetInstance().DevWriteBlock(buf, oldCpBlkId);
    ASSERT(ret >= 0);

    free(buf);
    free(newCp);
    HMFS_INFO("Done to rebuild checkpoint blocks\n");
    return SUCCESS_CODE;
}

int32_t ResizeOperator::HmfsResizeCheck(struct SuperBlockData *newSbData)
{
    if (newSbData == nullptr) {
        return EXIT_ERR_CODE;
    }
    struct CheckPointData *cpData = sbInfo_->ckptData;
    unsigned int overprovSegmentCount = (GetLeValue(newSbData->segmentCountInMain) -
        config_->newReservedSegments) * config_->newOverprovision / 100;
    overprovSegmentCount += config_->newReservedSegments;

    uint32_t userBlockCount = (GetLeValue(newSbData->segmentCountInMain) -
        overprovSegmentCount) * config_->blocksPerSegment;

    if (GetLeValue(cpData->validBlockCount) > userBlockCount) {
        return EXIT_ERR_CODE;
    }
    return SUCCESS_CODE;
}

int32_t ResizeOperator::FlushNatJournalEntries()
{
    struct CurSegmentInfo *curseg = CURSEG_I(sbInfo_, CURSEG_HOT_DATA);
    struct JournalEntry *journal = &curseg->segSumBlk->journal;
    int32_t ret = 0;
    int32_t i = 0;
    struct natBlockData *natBlock = static_cast<struct natBlockData *>(calloc(BLOCK_SZ, 1));
    ASSERT(natBlock);

    uint16_t natsSum = NatsInCursum(journal);

    for(; i < natsSum; i++) {
        uint32_t nid = LE32_TO_NATIVE(NidInJournal(journal, i));

        int32_t entryOff = nid % NAT_ENTRY_PER_BLOCK;
        unsigned long blockAddr = CurrentNatAddr(sbInfo_, nid, nullptr);
        ret = HmfsIo::GetInstance().DevReadBlock(natBlock, blockAddr);
        ASSERT(ret >= 0);

        memcpy(&natBlock->entries[entryOff], &NatInJournal(journal, i), sizeof(struct natEntry));

        ret = HmfsIo::GetInstance().DevWriteBlock(natBlock, blockAddr);
        ASSERT(ret >= 0);
    }

    free(natBlock);
    journal->nNats = 0;
    return i;
}

void ResizeOperator::RewriteCurrentSitPage(unsigned int segno, struct sitBlockData *sitBlk)
{
    if (sitBlk == nullptr) {
        return;
    }
    uint32_t blkAddr = CurrentSitAddr(sbInfo_, segno);
    ASSERT(HmfsIo::GetInstance().DevWriteBlock(sitBlk, blkAddr) >= 0);
}

int32_t ResizeOperator::FlushSitJournalEntries()
{
    struct CurSegmentInfo *curseg = CURSEG_I(sbInfo_, CURSEG_COLD_DATA);
    struct JournalEntry *journal = &curseg->segSumBlk->journal;
    struct SitInfo *sitInfo = SIT_I(sbInfo_);
    int32_t i;

    struct sitBlockData *sitBlk = static_cast<struct sitBlockData *>(calloc(BLOCK_SZ, 1));
    ASSERT(sitBlk);
    for (i = 0; i < SitsInCursum(journal); i++) {
        struct sitEntry *sit;
        struct SegEntry *se;

        unsigned int segno = SegnoInJournal(journal, i);
        se = GetSegEntry(sbInfo_, segno);

        GetCurrentSitPage(sbInfo_, segno, sitBlk);
        unsigned int sitEntryOffSet = segno % sitInfo->sentsPerBlock;
        sit = &sitBlk->entries[sitEntryOffSet];

        memcpy(sit->blockBitmap, se->curValidBitmap, SIT_VBLOCK_MAP_SIZE);
        sit->usedBlockCount = NATIVE_TO_LE16((se->type << SIT_VBLOCKS_SHIFT) | se->validBlocks);
        sit->modTime = NATIVE_TO_LE64(se->modTime);
        RewriteCurrentSitPage(segno, sitBlk);
    }
    free(sitBlk);
    journal->nSits = 0;
    return i;
}

void ResizeOperator::DuplicateCheckpoint()
{
    HMFS_DEBUG("Enter DuplicateCheckpoint");
    struct SuperBlockData *sbData = sbInfo_->rawSuper;
    unsigned long long dst;
    unsigned long long src;
    unsigned int segSize = 1 << GetLeValue(sbData->logBlksPerSeg);
    int32_t ret;

    if (sbInfo_->cpBackuped) {
        return;
    }
    void *buf = malloc(HMFS_BLKSIZE * segSize);
    ASSERT(buf);

    if (sbInfo_->curCpId == 1) {
        src = GetLeValue(sbData->cpBlkId);
        dst = src + segSize;
    } else {
        dst = GetLeValue(sbData->cpBlkId);
        src = dst + segSize;
    }

    ret = HmfsIo::GetInstance().DevRead(buf, src << HMFS_BLKSIZE_BITS, segSize << HMFS_BLKSIZE_BITS);
    ASSERT(ret >= 0);

    ret = HmfsIo::GetInstance().DevWrite(buf, dst << HMFS_BLKSIZE_BITS, segSize << HMFS_BLKSIZE_BITS);
    ASSERT(ret >= 0);

    free(buf);

    ret = HmfsFsyncDevice();
    ASSERT(ret >= 0);

    sbInfo_->cpBackuped = 1;
    HMFS_INFO("Duplicate valid checkpoint to mirror position "
        "%llu -> %llu\n", src, dst);
}

uint32_t ResizeOperator::GetFreeSegments()
{
    uint32_t freeSegs = 0;
    unsigned int mainSegments = sbInfo_->smInfoTable->mainSegments;
    for (uint32_t i = 0; i < mainSegments; i++) {
        struct SegEntry *se = GetSegEntry(sbInfo_, i);
        if (se->validBlocks == 0x0 && !IS_CUR_SEGNO(sbInfo_, i)) { //&&is_usable_seg(sbInfo_, i)
            freeSegs++;
        }
    }
    return freeSegs;
}

uint32_t ResizeOperator::UpdateNatBitsFlags(struct SuperBlockData *sbData,
    struct CheckPointData *cpData, uint32_t flags)
{
    if (sbData == nullptr || cpData == nullptr) {
        return 1;
    }

    uint32_t natBitsBytes =GetLeValue(sbData->segmentCountInNAT) << 5;
    uint32_t natBitsBlocks = HMFS_BYTES_TO_BLK((natBitsBytes << 1) + 8 + HMFS_BLKSIZE - 1);
    if (GetLeValue(cpData->cpPackBlockCount) <= (1 << GetLeValue(sbData->logBlksPerSeg)) - natBitsBlocks) {
        flags |= CP_NAT_BITS_FLAG;
    } else {
        flags &= (~CP_NAT_BITS_FLAG);
    }
    return flags;
}

uint32_t ResizeOperator::HmfsCheckpointChksum(struct CheckPointData *cpData)
{
    if (cpData == nullptr) {
        return 1;
    }
    unsigned int chksumOffset = LE32_TO_NATIVE(cpData->checksumOffset);
    uint32_t chksum = HmfsCommon::GetInstance().HmfsCalCrc32(HMFS_SUPER_MAGIC, cpData, chksumOffset);
    if (chksumOffset < CP_CHKSUM_OFFSET) {
        chksumOffset += sizeof(chksum);
        chksum = HmfsCommon::GetInstance().HmfsCalCrc32(chksum, (uint8_t *)cpData + chksumOffset, HMFS_BLKSIZE - chksumOffset);
    }
    return chksum;
}

uint64_t ResizeOperator::GetCpCrc(struct CheckPointData *cpData)
{
    if (cpData == nullptr) {
        return 1;
    }
    uint64_t cpVer = GetLeValue(cpData->cpVersion);
    size_t crcOffset = GetLeValue(cpData->checksumOffset);
    uint32_t crc = LE32_TO_NATIVE(*(uint32_t *)((unsigned char *)cpData + crcOffset));
    cpVer |= ((uint64_t)crc << 32);
    return NATIVE_TO_LE64(cpVer);
}

void ResizeOperator::WriteNatBits(struct SuperBlockData *sbData, struct CheckPointData *cpData, int set)
{
    if (sbData == nullptr || cpData == nullptr) {
        return;
    }
    struct NodeAddressTable *nat = NM_I(sbInfo_);
    uint32_t natBlocks = GetLeValue(sbData->segmentCountInNAT) << (GetLeValue(sbData->logBlksPerSeg) - 1);
    uint32_t natBitsBytes = natBlocks >> 3;
    uint32_t natBitsBlocks = HMFS_BYTES_TO_BLK((natBitsBytes << 1) + 8 + HMFS_BLKSIZE - 1);
    uint32_t blkaddr;
    int32_t ret;

    unsigned char *natBits = static_cast<unsigned char *>(calloc(HMFS_BLKSIZE, natBitsBlocks));
    ASSERT(natBits);

    struct natBlockData *natBlock = static_cast<struct natBlockData *>(malloc(HMFS_BLKSIZE));
    ASSERT(natBlock);

    unsigned char *fullNatBits = natBits + 8;
    unsigned char *emptyNatBits = fullNatBits + natBitsBytes;

    memset(fullNatBits, 0, natBitsBytes);
    memset(emptyNatBits, 0, natBitsBytes);

    for (uint32_t i = 0; i < natBlocks; i++) {
        int seg_off = i >> GetLeValue(sbData->logBlksPerSeg);
        int valid = 0;

        blkaddr = (unsigned long)(GetLeValue(sbData->natBlkId) +
            (seg_off << GetLeValue(sbData->logBlksPerSeg) << 1) +
            (i & ((1 << GetLeValue(sbData->logBlksPerSeg)) - 1)));

        /*
         * Should consider new nat_blocks is larger than old
         * nm_i->nat_blocks, since nm_i->nat_bitmap is based on
         * old one.
         */
        if (i < nat->natBlocks && HmfsCommon::GetInstance().HmfsTestBit(i, nat->natBitmap)) {
            blkaddr += (1 << GetLeValue(sbData->logBlksPerSeg));
        }

        ret = HmfsIo::GetInstance().DevReadBlock(natBlock, blkaddr);
        ASSERT(ret >= 0);

        for (uint32_t j = 0; j < NAT_ENTRY_PER_BLOCK; j++) {
            if ((i == 0 && j == 0) || natBlock->entries[j].blockId != NULL_ADDR) {
                valid++;
            }
        }
        if (valid == 0) {
            HmfsCommon::GetInstance().TestAndSetBitLe(i, emptyNatBits);
        } else if (valid == NAT_ENTRY_PER_BLOCK) {
            HmfsCommon::GetInstance().TestAndSetBitLe(i, fullNatBits);
        }
    }
    *(uint64_t *)natBits = GetCpCrc(cpData);
    free(natBlock);

    blkaddr = GetLeValue(sbData->segment0BlkId) + (set <<
        GetLeValue(sbData->logBlksPerSeg)) - natBitsBlocks;

    HMFS_INFO("\tWriting NAT bits pages, at offset 0x%08x\n", blkaddr);

    for (uint32_t i = 0; i < natBitsBlocks; i++) {
        if (HmfsIo::GetInstance().DevWriteBlock(natBits + i * HMFS_BLKSIZE, blkaddr + i))
            HMFS_ERROR("\tError: write NAT bits to disk!!!\n");
    }
    HMFS_INFO("Write valid nat_bits in checkpoint\n");

    free(natBits);
}

int ResizeOperator::HmfsFsyncDevice()
{
#ifdef HAVE_FSYNC
    for (int i = 0; i < config_->nDevices; i++) {
        if (fsync(config_->devices[i].fd) < 0) {
            HMFS_ERROR("\tError: Could not conduct fsync!!!\n");
            return -1;
        }
    }
#endif
    return 0;
}

void ResizeOperator::WriteCheckpoint()
{
    HMFS_DEBUG("Enter WriteCheckpoint");
    struct SuperBlockData *sbData = sbInfo_->rawSuper;
    struct CheckPointData *cpData = sbInfo_->ckptData;
    uint32_t orphanBlks = 0;
    uint32_t flags = CP_UMOUNT_FLAG;
    int32_t ret;
    if (IsCheckpointFlags(cpData, CP_ORPHAN_PRESENT_FLAG)) {
        orphanBlks = LE32_TO_NATIVE(cpData->cpPackStartSum) - 1;
        flags |= CP_ORPHAN_PRESENT_FLAG;
    }
    if (IsCheckpointFlags(cpData, CP_TRIMMED_FLAG)) {
        flags |= CP_TRIMMED_FLAG;
    }
    if (IsCheckpointFlags(cpData, CP_DISABLED_FLAG)) {
        flags |= CP_DISABLED_FLAG;
    }
    if (IsCheckpointFlags(cpData, CP_LARGE_NAT_BITMAP_FLAG)) {
        flags |= CP_LARGE_NAT_BITMAP_FLAG;
        SetLeValue(cpData->checksumOffset, CP_MIN_CHKSUM_OFFSET);
    } else {
        SetLeValue(cpData->checksumOffset, CP_CHKSUM_OFFSET);
    }
    SetLeValue(cpData->freeSegmentCount, GetFreeSegments());
    SetLeValue(cpData->validBlockCount, sbInfo_->totalValidBlockCount);
    SetLeValue(cpData->validNodeCount, sbInfo_->totalValidNodeCount);
    SetLeValue(cpData->validInodeCount, sbInfo_->totalValidInodeCount);
    SetLeValue(cpData->cpPackBlockCount, 8 + orphanBlks + GetLeValue(sbData->cpPayload));

    flags = UpdateNatBitsFlags(sbData, cpData, flags);
    SetLeValue(cpData->cpFlags, flags);

    uint32_t crc = HmfsCheckpointChksum(cpData);
    *((uint32_t *)((unsigned char *)cpData + GetLeValue(cpData->checksumOffset))) = NATIVE_TO_LE32(crc);

    unsigned long long cpBlkNo = GetLeValue(sbData->cpBlkId);
    if (sbInfo_->curCpId == 2) {
        cpBlkNo += 1 << GetLeValue(sbData->logBlksPerSeg);
    }
    /* write the first cpData */
    ret = HmfsIo::GetInstance().DevWriteBlock(cpData, cpBlkNo++);
    ASSERT(ret >= 0);

    /* skip payload */
    cpBlkNo += GetLeValue(sbData->cpPayload);
    /* skip orphan blocks */
    cpBlkNo += orphanBlks;

    /* update summary blocks having nullified journal entries */
    for (int32_t i = 0; i < NO_CHECK_TYPE; i++) {
        struct CurSegmentInfo *curseg = CURSEG_I(sbInfo_, i);
        ret = HmfsIo::GetInstance().DevWriteBlock(curseg->segSumBlk, cpBlkNo++);
        ASSERT(ret >= 0);

        if (!(GetLeValue(sbData->features) & NATIVE_TO_LE32(HMFS_FEATURE_RO))) {
            /* update original SSA too */
            uint64_t ssaBlk = sbInfo_->smInfoTable->ssaBlkaddr + curseg->segNum;
            ret = HmfsIo::GetInstance().DevWriteBlock(curseg->segSumBlk, ssaBlk);
            ASSERT(ret >= 0);
        }
    }

    /* Write nat bits */
    if (flags & CP_NAT_BITS_FLAG) {
        WriteNatBits(sbData, cpData, sbInfo_->curCpId);
    }
    /* in case of sudden power off */
    ret = HmfsFsyncDevice();
    ASSERT(ret >= 0);
    // /* write the last cpData */
    ret = HmfsIo::GetInstance().DevWriteBlock(cpData, cpBlkNo++);
    ASSERT(ret >= 0);
    ret = HmfsFsyncDevice();
    ASSERT(ret >= 0);
}

void ResizeOperator::DupWriteCheckpoints()
{
    /* copy valid checkpoint to its mirror position */
    DuplicateCheckpoint();
    /* repair checkpoint at CP #0 position */
    sbInfo_->curCpId = 1;
    WriteCheckpoint();
}

void ResizeOperator::FlushJournalEntries()
{
    HMFS_DEBUG("Enter FlushJournalEntries");
    int32_t nNats = FlushNatJournalEntries();
    int32_t nSits = FlushSitJournalEntries();
    if (nNats || nSits) {
        DupWriteCheckpoints();
    }
}

void ResizeOperator::UpdateSuperBlock(struct SuperBlockData *superBlock, int sbMask)
{
    HMFS_DEBUG("Enter UpdateSuperBlock");
    if (superBlock == nullptr) {
        return;
    }
    uint8_t *buf = static_cast<uint8_t *>(calloc(BLOCK_SZ, 1));
    ASSERT(buf);
    if (GetLeValue(superBlock->features) & HMFS_FEATURE_SB_CHKSUM) {
        uint32_t oldCrc = GetLeValue(superBlock->checksum);
        uint32_t newCrc = HmfsCommon::GetInstance().HmfsCalCrc32(HMFS_SUPER_MAGIC, superBlock, SB_CHKSUM_OFFSET);
        SetLeValue(superBlock->checksum, newCrc);
        HMFS_INFO("SueprBlock CRC is updated (0x%x -> 0x%x)\n", oldCrc, newCrc);
    }

    memcpy(buf + HMFS_SUPER_OFFSET, superBlock, sizeof(*superBlock));
    for (int addr = SB0_ADDR; addr < SB_MAX_ADDR; addr++) {
        if (SB_MASK(addr) & sbMask) {
            int ret = HmfsIo::GetInstance().DevWriteBlock(buf, addr);
            ASSERT(ret >= 0);
        }
    }
    free(buf);
    HMFS_INFO("Done to update superblock\n");
}

int32_t ResizeOperator::HmfsResizeExpand()
{
    HMFS_DEBUG("Enter HmfsResizeExpand");
    struct SuperBlockData *sbData = sbInfo_->rawSuper;
    struct SuperBlockData newSbRaw;
    struct SuperBlockData *newSb = &newSbRaw;
    unsigned int offsetSeg = 0;
    int32_t err = -1;

    FlushJournalEntries();

    memcpy(newSb, sbData, sizeof(*newSb));
    if (GetNewSuperBlock(newSb)) {
        return -1;
    }
    if (HmfsResizeCheck(newSb) < 0) {
        return -1;
    }
    /* check nat availability */
    if (GetLeValue(sbData->segmentCountInNAT) > GetLeValue(newSb->segmentCountInNAT)) {
        err = ShrinkNats(newSb);
        if (err) {
            HMFS_ERROR("\tError: Failed to shrink NATs\n");
            return err;
        }
    }

    uint64_t oldMainBlkaddr = GetLeValue(sbData->mainBlkId);
    uint64_t newMainBlkaddr = GetLeValue(newSb->mainBlkId);
    uint64_t offset = newMainBlkaddr - oldMainBlkaddr;
    uint32_t endBlkaddr = (GetLeValue(sbData->segmentCountInMain) <<
            GetLeValue(sbData->logBlksPerSeg)) + GetLeValue(sbData->mainBlkId);

    err = -EAGAIN;
    if (newMainBlkaddr < endBlkaddr) {
        err = reDefrag_->HmfsDefragment(oldMainBlkaddr, offset, newMainBlkaddr, 0);
        if (!err) {
            offsetSeg = offset >> GetLeValue(sbData->logBlksPerSeg);
        }
        HMFS_INFO("Try to do defragement: %s\n", err ? "Skip": "Done");
    }
    /* move whole data region */
    if (err) {
        MigrateMainArea(offset);
    }
    MigrateSsa(newSb, offsetSeg);
    MigrateNat(newSb);
    MigrateSit(newSb, offsetSeg);
    RebuildCheckpoint(newSb, offsetSeg);
    UpdateSuperBlock(newSb, SB_MASK_ALL);
    // print_raw_sb_info(sb);
    // print_raw_sb_info(newSb);
    return SUCCESS_CODE;
}

int32_t ResizeOperator::HmfsResizeShrink()
{
    HMFS_DEBUG("Enter HmfsResizeShrink");
    struct SuperBlockData *sbData = sbInfo_->rawSuper;
    struct SuperBlockData newSbRaw;
    struct SuperBlockData *newSb = &newSbRaw;
    int32_t err = -1;

    /* flush NAT/SIT journal entries */
    FlushJournalEntries();

    memcpy(newSb, sbData, sizeof(*newSb));
    if (GetNewSuperBlock(newSb)){
        return -1;
    }
    if (HmfsResizeCheck(newSb) < 0) {
        return -1;
    }
    /* check nat availability */
    if (GetLeValue(sbData->segmentCountInNAT) > GetLeValue(newSb->segmentCountInNAT)) {
        err = ShrinkNats(newSb);
        if (err) {
            HMFS_ERROR("\tError: Failed to shrink NATs\n");
            return err;
        }
    }

    uint32_t oldMainBlkaddr = GetLeValue(sbData->mainBlkId);
    uint32_t newMainBlkaddr = GetLeValue(newSb->mainBlkId);
    unsigned int offset = oldMainBlkaddr - newMainBlkaddr;
    uint64_t oldEndBlkaddr = (GetLeValue(sbData->segmentCountInMain) <<
        GetLeValue(sbData->logBlksPerSeg)) + GetLeValue(sbData->mainBlkId);
    uint32_t newEndBlkaddr = (GetLeValue(newSb->segmentCountInMain) <<
        GetLeValue(newSb->logBlksPerSeg)) + GetLeValue(newSb->mainBlkId);

    uint64_t tmpEndBlkaddr = newEndBlkaddr + offset;
    err = reDefrag_->HmfsDefragment(tmpEndBlkaddr, oldEndBlkaddr - tmpEndBlkaddr, tmpEndBlkaddr, 1);
    HMFS_INFO("Try to do defragement: %s\n", err ? "Insufficient Space": "Done");

    if (err) {
        return -ENOSPC;
    }

    UpdateSuperBlock(newSb, SB_MASK_ALL);
    RebuildCheckpoint(newSb, 0);
    
    return SUCCESS_CODE;
}

int32_t ResizeOperator::HmfsResize()
{
    HMFS_DEBUG("Enter HmfsResize");
    if (CreateDeviceInfo()) {
        HMFS_ERROR("CreateDeviceInfo fail");
        return -1;
    }
    if(InitDeviceInfoToConfig()) {
        HMFS_ERROR("InitDeviceInfoToConfig fail");
        return -1;
    }
    ResizeLoad resizeLoad(config_, sbInfo_);
    int32_t ret = resizeLoad.HmfsLoadData();
    if(ret) {
        return EXIT_ERR_CODE;
    }
    if (!config_->targetSectors) {
        config_->targetSectors = config_->totalSectors;
    }
    if (config_->targetSectors > config_->totalSectors) {
        HMFS_ERROR("Out-of-range Target=0x%" PRIx64 " / 0x%" PRIx64 "", config_->targetSectors, config_->totalSectors);
        return EXIT_ERR_CODE;
    }
    struct SuperBlockData *sbData = sbInfo_->rawSuper;
    uint64_t configBlockCount = config_->targetSectors * config_->sectorSize >> GetLeValue(sbData->logBlockSize);
    uint64_t curBlockCount = GetLeValue(sbData->blockCount);
    if (configBlockCount < curBlockCount) {
        if (!config_->safeResize) {
            HMFS_ERROR("Nothing to resize, now only supports resizing with safe resize flag\n");
            return EXIT_ERR_CODE;
        } else {
            return HmfsResizeShrink();
        }
    } else if ((configBlockCount > curBlockCount) || config_->force) {
        return HmfsResizeExpand();
    } else {
        HMFS_INFO("Nothing to resize.\n");
        return SUCCESS_CODE;
    }
    resizeLoad.HmfsUnLoadData();
}

int32_t ResizeOperator::CreateDeviceInfo()
{
    for (auto i = 0; i < resizeConfig_.deviceList.size(); i++) {
        if (DeviceManager::GetInstance().CreateDeviceInfo(resizeConfig_.deviceList[i], i == 0)) {
            return -1;
        }
    }
    return 0;
}

int32_t ResizeOperator::InitDeviceInfoToConfig()
{
    for (uint32_t i = 0; i < resizeConfig_.deviceList.size(); i++) {
        DeviceInfo *deviceInfo = DeviceManager::GetInstance().GetDeviceInfo(i);
        if (deviceInfo == nullptr) {
            HMFS_DEBUG("failed to get device info of %s", resizeConfig_.deviceList[i].c_str());
            continue;
        }
        config_->devices[i].path = deviceInfo->path;
        config_->devices[i].fd = deviceInfo->fd;
        if (i == 0) {
            config_->sectorSize = deviceInfo->sectorSize;
            config_->sectorsPerBlock = HMFS_BLOCK_SIZE / config_->sectorSize;
        } else {
            if (deviceInfo->sectorSize != config_->sectorSize) {
                HMFS_ERROR("different sector sizes");
                return -1;
            }
        }
        config_->totalSectors += deviceInfo->sectorCount;
    }
    config_->nDevices = DeviceManager::GetInstance().GetDeviceCount();
    if(config_->totalSectors == 0) {
        return -1;
    }
    return 0;
}

}
}
